package org.gov.vis.util;

import java.math.BigDecimal;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * This class contains some common helper methods for validation
*/

public class ValidatorUtils {

	public static final String ALPHABETS = " ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	public static final String NUMERICS = "0123456789";
	public static final String DECIMALS = "0123456789.";
	public static final int NO_MAXLENGTH = Integer.MIN_VALUE;
	public static final String ALPHA_NUMERIC ="^[a-zA-Z0-9 ]$";
	public static final String CHARACTERONLY ="^[a-zA-Z& ]$";
	public static final String NUMBERSONLY ="^[0-9+-]$";

	/**
	 * Validates the passed String _value for min and max length, and 
	 * returns a string containing error message, if validation fails.
	 * <br>
	 * _trim specifies whether _value should be trimmed, before the validation
	 * <br>
	 * returns null, if all validations are correct.
	 */
	public static String validateString(String _fldNm, String _value, int _minLength, int _maxLength, boolean _trim) {

		//check for null, only if the min length is not 0
		if ( (_value == null || "".equals(_value))) {
			if (_minLength != 0) {
				return _fldNm + " is required.\n";

			}
			else
				return null;
		}

		if (_trim)
			_value = _value.trim();

		// check for minimum length
		int len = _value.length();
		if (len < _minLength) {
			StringBuffer sb = new StringBuffer(_fldNm);
			sb.append(" should be");
			if (_minLength == _maxLength)
				sb.append(" exactly ");
			else
				sb.append(" at-least ");
			sb.append(_minLength);
			sb.append(" characters long.");

			return sb.toString();
		}

		//check for max length only if it is required
		if ((_maxLength != NO_MAXLENGTH) && (len > _maxLength)) {
			StringBuffer sb = new StringBuffer(_fldNm);
			sb.append(" should be");
			if (_minLength == _maxLength)
				sb.append(" exactly ");
			else
				sb.append(" less than ");
			sb.append(_maxLength);
			sb.append(" characters long.");

			return sb.toString();
		}

		return null;
	} //end validateString


	/**
	 * Validates the passed String _value for the passed _allowedChars,
	 * min and max length, and returns a String containing error 
	 * messages, if validation fails.
	 * <br>
	 * _allowedChars contains all the possible characters that are allowed
	 * <br>
	 * _trim specifies whether _value should be trimmed, before the validation
	 * <br>
	 * returns null, if all validations are correct.
	 */
	public static String validateStringFor(
			String _fldNm,
			String _value,
			String _allowedChars,
			int _minLength,
			int _maxLength,
			boolean _trim, boolean required) {

		String errorMessage = null;
		String _tempValue = "";
		// List<Object> errorList = new ArrayList<Object>(2); //max of 2 messages from this method

		//check for null
		if ((_value == null || "".equals(_value)) && (required)) {
			if (_minLength != 0) {
				return errorMessage =_fldNm + "";
			}
			else
				return null;
		} // end of checking null string

		if (_trim)
			_value = _value.trim();



		for (int i = 0; i < _value.length(); i++)
		{
			if (!Character.isLetterOrDigit(_value.charAt(i))){
				_tempValue=_tempValue+_value.charAt(i);
			}
		}


		for (int i = 0; i < _tempValue.length(); i++)
			if (_allowedChars.indexOf(Character.toUpperCase(_tempValue.charAt(i))) == -1) {
				errorMessage = _fldNm + " contains invalid characters.";
				break;
			}

		String strErrLst = validateStrings(_fldNm, _value, _minLength, _maxLength, _trim, required);

		if (strErrLst != null)
			errorMessage= ( (errorMessage== null) ? strErrLst : errorMessage+"\n"+strErrLst);


		return errorMessage;
	} //end validateStringFor


	/**
	 * Validates that the passed number satisfies the min and max values, and returns a String
	 * containing error messages, if validation fails.
	 */
	public static String validateNumber(String _fldNm, long _value, long _minValue, long _maxValue) {
		String errorMessage= null;

		if (_value < _minValue) {
			StringBuffer sb = new StringBuffer(_fldNm);
			sb.append(" should be");
			if (_minValue == _maxValue)
				sb.append(" equal to ");
			else
				sb.append(" greater than ");
			sb.append(_minValue);
			sb.append(".");

			errorMessage = sb.toString();
		}

		if (_value > _maxValue) {
			StringBuffer sb = new StringBuffer(_fldNm);
			sb.append(" should be ");

			if (_minValue == _maxValue)
				sb.append(" equal to ");
			else
				sb.append(" less than ");
			sb.append(_maxValue);
			sb.append(".");
			errorMessage= ( (errorMessage== null) ? sb.toString() : errorMessage+sb.toString() );

			//errorList.add(sb.toString());
		}

		return errorMessage;
	} //end validateNumber


	/**
	 * Validates that the passed number satisfies the min and max values, and returns a String
	 * containing error messages, if validation fails.
	 */
	public static String validateNumber(String _fldNm, Long _value, long _minValue, long _maxValue) {
		String errorMessage=null;

		//check for null
		if (_value == null) {
			errorMessage =_fldNm + " should not be empty.";
			return errorMessage;           
		}

		if (_value < _minValue ) {
			StringBuffer sb = new StringBuffer(_fldNm);
			sb.append(" should be");
			if (_minValue == _maxValue)
				sb.append(" equal to ");
			else
				sb.append(" greater than or equal to ");
			sb.append(_minValue);
			sb.append(".");

			errorMessage= ( (errorMessage== null) ? sb.toString() : errorMessage+sb.toString() );
		}

		if (_value > _maxValue) {
			StringBuffer sb = new StringBuffer(_fldNm);
			sb.append(" should be ");

			if (_minValue == _maxValue)
				sb.append(" equal to ");
			else
				sb.append(" less than ");
			sb.append(_maxValue);
			sb.append(".");

			errorMessage= ( (errorMessage== null) ? sb.toString() : errorMessage+sb.toString() );
		}

		return errorMessage;
	} //end validateNumber


	/**
	 * Validates that the passed decimal field satisfies the min and max values
	 * and the number of decimal places, and returns a String
	 * containing error messages, if validation fails.
	 */
	public static String validateDecimal(
			String _fldNm,
			double _value,
			double _minValue,
			double _maxValue,
			int _minDecimals,
			int _maxDecimals) {

		//List<Object> errorList = new ArrayList<Object>(2);
		String  errorMessage =null;
		
		StringBuffer sb = new StringBuffer(_fldNm);
		
		if (_value < _minValue) {
			sb.append(" should be");
			if (_minValue == _maxValue)
				sb.append(" equal to ");
			else
				sb.append(" greater than ");
			sb.append(_minValue);
			sb.append(".");

			errorMessage = sb.toString();
			
		}else if (_value > _maxValue) {
			sb.append(" should be ");

			if (_minValue == _maxValue)
				sb.append(" equal to ");
			else
				sb.append(" less than ");
			sb.append(_maxValue);
			sb.append(".");

			errorMessage= ( (errorMessage == null) ? sb.toString() : errorMessage+"\n"+sb.toString() );
			
		}
		//check for decimal places
		String str = Double.toString(_value);
		int decimalPlaces = str.length() - str.indexOf('.') - 1;


		if (decimalPlaces < _minDecimals) {
			sb.append(" should contain");
			if (_minDecimals == _maxDecimals)
				sb.append(" exactly ");
			else
				sb.append(" at-least ");
			sb.append(_minDecimals);
			sb.append(" decimal places.");

			errorMessage= ((errorMessage == null) ? sb.toString() : errorMessage+"\n"+sb.toString() );			
		}else if (decimalPlaces > _maxDecimals) {
			
			sb.append(" should contain");
			if (_minDecimals == _maxDecimals)
				sb.append(" exactly ");
			else
				sb.append(" less than ");
			sb.append(_maxDecimals);
			sb.append(" decimal places.");

			errorMessage= ((errorMessage == null) ? sb.toString() : errorMessage+"\n"+sb.toString() );
		}
		
		
		return errorMessage;
	} //end validateDecimal



	/**
	 * Validates that the passed decimal field satisfies the min and max values
	 * and the number of decimal places, and returns a list of Strings
	 * containing error messages, if validation fails.
	 */
	private static String validateDecimal(String _fldNm, BigDecimal _value,
			BigDecimal _minValue, BigDecimal _maxValue, int _minDecimals,
			int _maxDecimals) {

		//List<Object> errorList = new ArrayList<Object>(2);
		String errorMessage = "";

		if ((_value.compareTo(_minValue) == -1)) {
			StringBuffer sb = new StringBuffer(_fldNm);
			sb.append(" should be");
			if ((_minValue.compareTo(_maxValue) == 0))
				sb.append(" equal to ");
			else
				sb.append(" greater than ");
			sb.append(_minValue);
			sb.append(".");

			errorMessage= ((errorMessage == null) ? sb.toString() : errorMessage+"\n"+sb.toString() );
		}

		if (_value.compareTo(_maxValue) == 1) {
			StringBuffer sb = new StringBuffer(_fldNm);
			sb.append(" should be ");

			if (_minValue == _maxValue)
				sb.append(" equal to ");
			else
				sb.append(" less than ");
			sb.append(_maxValue);
			sb.append(".");

			errorMessage= ((errorMessage == null) ? sb.toString() : errorMessage+"\n"+sb.toString() );
		}


		// check for decimal places
		//String str = Double.toString(_value.doubleValue());
		String str = _value.toString();
		if(str.indexOf('.') != -1){
			int decimalPlaces = str.length() - str.indexOf('.') - 1;

			if (decimalPlaces < _minDecimals) {
				StringBuffer sb = new StringBuffer(_fldNm);
				sb.append(" should contain");
				if (_minDecimals == _maxDecimals)
					sb.append(" exactly ");
				else
					sb.append(" at-least ");
				sb.append(_minDecimals);
				sb.append(" decimal places.");

				errorMessage= ((errorMessage == null) ? sb.toString() : errorMessage+"\n"+sb.toString() );
			}

			if (decimalPlaces > _maxDecimals) {
				StringBuffer sb = new StringBuffer(_fldNm);
				sb.append(" should contain");
				if (_minDecimals == _maxDecimals)
					sb.append(" exactly ");
				else
					sb.append(" less than ");
				sb.append(_maxDecimals);
				sb.append(" decimal places.");

				errorMessage= ((errorMessage == null) ? sb.toString() : errorMessage+"\n"+sb.toString() );
			}
		}
		
		return errorMessage;
	} // end validateDecimal


	/**
	 * checks whether the passed string contains a valid e-mail address
	 */
	public static String validateEmail(String _fldNm, String _value, boolean _required) {
		//max of 1 message from this method
		String errorString = null;
		//check for null
		if ((_value == null || "".equals(_value)) && (_required)) {
			errorString=_fldNm + "";
			return errorString;
		}



		boolean validAddress = false;
		int atIndex = _value.indexOf('@');
		if (atIndex > 0) {
			int periodIndex = _value.indexOf(".", atIndex);
			if ((periodIndex > atIndex + 1) && (_value.length() > periodIndex + 1))
				validAddress = true;
		}

		if (!validAddress)
			errorString =_fldNm + " should be a valid e-mail address.";

		return errorString;
	} //end validateEmail


	
	public static String validateAlphaNumeric(String _fldNm,
			String _value,
			int _minLength,
			int _maxLength,
			boolean _trim, boolean required){
		return validateStringFor(_fldNm, _value, ALPHA_NUMERIC, _minLength, _maxLength, _trim, required);
		
	}
	
	public static String validateCharacter(String _fldNm, String _value, int _minLength, int _maxLength, boolean _trim, boolean required){
		return validateCharacter(_fldNm, _value, CHARACTERONLY, _minLength, _maxLength, _trim, required);
	}
	
	public static String validateOnlyNumber(String _fldNm, String _value, int _minLength, int _maxLength, boolean _trim, boolean required){
		return validateOnlyNumbers(_fldNm, _value, NUMBERSONLY, _minLength, _maxLength, _trim, required);
	}

	/**
	 * Validates that the passed selected drop-down/pull-up element and returns a list of Strings
	 * containing error messages, if validation fails.
	 */
	public static String validateDropDown(String _fldNm, long _value, long _minValue) {
		String errorMessage= null;                
		if (_value < _minValue) 
			return	_fldNm+" is required.\n";    

		return errorMessage;
	} //end validateDropDown

	
	/**
	 * Validates that the passed date and returns a String
	 * containing error messages, if validation fails.
	 */
	public static String validateDate(String _fldNm, String _dateStr) {		
		String errorMessage= null; 
		if(_dateStr == null || _dateStr.equals(""))			
			return errorMessage = _fldNm + "";   
		return errorMessage;
	} //end validateDate
	
	
	
	/**
	 * Validates that the passed date and returns a String
	 * containing error messages, if validation fails.
	 */
	private static String validateDateFormat(String _fldNm, String _dateStr) {		
		String errorMessage= null; 
		if(_dateStr == null || _dateStr.equals(""))			
			return errorMessage = _fldNm + " is required.";   

		String expression = "^[0-1][1-9][- / ]?(0[1-9]|[12][0-9]|3[01])[- /]?(18|19|20|21)\\d{2}$";  
		 
		/* * ^[0-1][1-9] : The month starts with a 0 and a digit between 1 and 9 
		 * [- / ]?: Followed by  an optional "-" or "/". 
		 * (0[1-9]|[12][0-9]|3[01]) : The day part must be either between 01-09, or 10-29 or 30-31. 
		 * [- / ]?: Day part will be followed by  an optional "-" or "/". 
		 * (18|19|20|21)\\d{2}$ : Year begins with either 18, 19, 20 or 21 and ends with two digits. 
		*/
		CharSequence inputStr = _dateStr;  
		Pattern pattern = Pattern.compile(expression,Pattern.CASE_INSENSITIVE);  
		Matcher matcher = pattern.matcher(inputStr);  
		if(matcher.matches()){  
			errorMessage = _fldNm + " is invalid.";
		}

		return errorMessage;
	} //end validateDate
	
	public static String validateCharacter(
			String _fldNm,
			String _value,
			String _allowedChars,
			int _minLength,
			int _maxLength,
			boolean _trim, boolean required) {

		String errorMessage = null;
		String _tempValue = "";
		// List<Object> errorList = new ArrayList<Object>(2); //max of 2 messages from this method

		//check for null
		if ((_value == null || "".equals(_value)) && (required)) {
			if (_minLength != 0) {
				return errorMessage =_fldNm + "";
			}
			else
				return null;
		} // end of checking null string

		if (_trim)
			_value = _value.trim();



		for (int i = 0; i < _value.length(); i++)
		{
			if (!Character.isLetter(_value.charAt(i))){
				_tempValue=_tempValue+_value.charAt(i);
			}
		}


		for (int i = 0; i < _tempValue.length(); i++)
			if (_allowedChars.indexOf(Character.toUpperCase(_tempValue.charAt(i))) == -1) {
				errorMessage = _fldNm + " will accepts only characters.";
				break;
			}

		String strErrLst = validateStrings(_fldNm, _value, _minLength, _maxLength, _trim, required);

		if (strErrLst != null)
			errorMessage= ( (errorMessage== null) ? strErrLst : errorMessage+"\n"+strErrLst);


		return errorMessage;
	} //end validateCharacter
	
	public static String validateOnlyNumbers(
			String _fldNm,
			String _value,
			String _allowedChars,
			int _minLength,
			int _maxLength,
			boolean _trim, boolean required) {

		String errorMessage = null;
		String _tempValue = "";
		// List<Object> errorList = new ArrayList<Object>(2); //max of 2 messages from this method

		//check for null
		if ((_value == null || "".equals(_value)) && (required)) {
			if (_minLength != 0) {
				return errorMessage =_fldNm + "";
			}
			else
				return null;
		} // end of checking null string

		if (_trim)
			_value = _value.trim();



		for (int i = 0; i < _value.length(); i++)
		{
			if (!Character.isDigit(_value.charAt(i))){
				_tempValue=_tempValue+_value.charAt(i);
			}
		}


		for (int i = 0; i < _tempValue.length(); i++)
			if (_allowedChars.indexOf(Character.toUpperCase(_tempValue.charAt(i))) == -1) {
				errorMessage = _fldNm + " will accepts only Numbers.";
				break;
			}

		String strErrLst = validateStrings(_fldNm, _value, _minLength, _maxLength, _trim, required);

		if (strErrLst != null)
			errorMessage= ( (errorMessage== null) ? strErrLst : errorMessage+"\n"+strErrLst);


		return errorMessage;
	} //end validateOnlyNumbers
	
	public static String validateDecimal(
			String _fldNm,
			String _value,
			String _allowedChars,
			double _minValue,
			double _maxValue,
			int _minDecimals,
			int _maxDecimals,
			boolean _trim, boolean required) {

		String errorMessage = null;
		String _tempValue = "";
		// List<Object> errorList = new ArrayList<Object>(2); //max of 2 messages from this method

		//check for null
		if (_value == null || "".equals(_value)) {		
			if(required)
				return errorMessage =_fldNm + "";	
			else
				return null;
		} // end of checking null string

		if (_trim)
			_value = _value.trim();

		for (int i = 0; i < _value.length(); i++)
		{
			if (!Character.isDigit(_value.charAt(i))){
				_tempValue=_tempValue+_value.charAt(i);
			}
		}


		for (int i = 0; i < _tempValue.length(); i++)
			if (_allowedChars.indexOf(Character.toUpperCase(_tempValue.charAt(i))) == -1) {
				errorMessage = _fldNm + " will accepts only decimal values.";
				break;
			}

		if (errorMessage != null)
			return errorMessage;
		
		String strErrLst = validateDecimal(_fldNm, Double.parseDouble(_value), _minValue, _maxValue, _minDecimals, _maxDecimals);
		if (strErrLst != null)
			errorMessage= ( (errorMessage== null) ? strErrLst : errorMessage+"\n"+strErrLst);
		
		return errorMessage;
		
	} //end validateOnlyNumbers
	
	
	/**
	 * Validates the passed String _value for min and max length, and 
	 * returns a string containing error message, if validation fails.
	 * <br>
	 * _trim specifies whether _value should be trimmed, before the validation
	 * <br>
	 * returns null, if all validations are correct.
	 */
	public static String validateStrings(String _fldNm, String _value, int _minLength, int _maxLength, boolean _trim, boolean required) {

//System.out.println(""+_fldNm+"---"+_value+ "----" +_minLength+"---"+_maxLength+"---"+true);
		//check for null, only if the min length is not 0
		if ( (_value == null || "".equals(_value)) && (required)) {
			if (_minLength != 0) {
				return _fldNm + "";

			}
			else
				return null;
		}

		if (_trim)
			_value = _value.trim();

		// check for minimum length
		int len = _value.length();
		if(!required && len==0)
			return null;
		
		if (len < _minLength) {
			StringBuffer sb = new StringBuffer(_fldNm);
			sb.append(" should be");
			if (_minLength == _maxLength)
				sb.append(" exactly ");
			else
				sb.append(" at-least ");
			sb.append(_minLength);
			sb.append(" characters long.");

			return sb.toString();
		}

		//check for max length only if it is required
		if ((_maxLength != NO_MAXLENGTH) && (len > _maxLength)) {
			StringBuffer sb = new StringBuffer(_fldNm);
			sb.append(" should be");
			if (_minLength == _maxLength)
				sb.append(" exactly ");
			else
				sb.append(" less than ");
			sb.append(_maxLength);
			sb.append(" characters long.");

			return sb.toString();
		}

		return null;
	} //end validateStrings



} //end class